#include <stdio.h>
#include <stdlib.h>
#include "buffer.h"
#include "flv.h"
#include "mp3.h"

FLVAPI int bitrateTbl[16][BITRATE_NUM] =
{
 	{ -1,  -1},{ 32,   8},{ 40,  16},{ 48,  24},
	{ 56,  32},{ 64,  40},{ 80,  48},{ 96,  56},
	{112,  64},{128,  80},{160,  96},{192, 112},
	{224, 128},{256, 144},{320, 160},{ -1,  -1}
};

FLVAPI int samplingRateTbl[3][SAMPLERATE_NUM] =
{
	{ 44100, 22050, 11025 },
	{ 48000, 24000, 12000 },
	{ 32000, 16000,  8000 }
};
struct FLVAPI mp3_frame {
	ui version;
	ui channel;
	int bitrate;
 	int samplingRate;
	int samples;
	int size;
	const unsigned char *data;
};	
 
FLVAPI int checkHdr(ui hdr)
{
    if( (hdr & MP3_FRAME_SYNC) != MP3_FRAME_SYNC
	    || ((hdr>>SHIFT_VERSION) & 0x3) == 1
	    || ((hdr>>SHIFT_LAYER) & 0x3) == 0
	    || ((hdr>>SHIFT_BITRATE) & 0xf) == 0xf 
	    || ((hdr>>SHIFT_BITRATE) & 0xf) == 0
	    || ((hdr>>SHIFT_SAMPLERATE)&0x3) == 0x3
	    || (hdr & 0xffff0000) == 0xfffe0000)
        return 0;
    return 1;
}

FLVAPI int getNextFrame(Mp3Audio *mp3, struct mp3_frame *frame)
{
	ui hdr, br_idx, sr_idx;	
	int ret;
	int factor;	
	do
	{
		ret = mp3->buf->ReadUI32(&hdr);
		if(ret < 0) return ret;
		if(checkHdr(hdr)) break;
		mp3->buf->Seek(-3, BUF_CUR);
	} while (1);
	frame->version = (hdr >> SHIFT_VERSION) & 0x3;
	br_idx = (hdr >> SHIFT_BITRATE) & 0xf;
	sr_idx = (hdr >> SHIFT_SAMPLERATE) & 0x3;
	switch(frame->version)
	{
		case MPEG_25:
			frame->bitrate = bitrateTbl[br_idx][BITRATE_MPEG2];
			frame->samplingRate = samplingRateTbl[sr_idx][SAMPLERATE_MPEG25];
			factor = 72;
			break;
		case MPEG_2:
			frame->bitrate = bitrateTbl[br_idx][BITRATE_MPEG2];
			frame->samplingRate = samplingRateTbl[sr_idx][SAMPLERATE_MPEG2];
			factor = 72;
			break;
		case MPEG_1:
			frame->bitrate = bitrateTbl[br_idx][BITRATE_MPEG1];
			frame->samplingRate = samplingRateTbl[sr_idx][SAMPLERATE_MPEG1];
			factor = 144;
			break;
		default:
			printf("invalid header %x\n", hdr);
			return -1;
	}
	frame->size = (factor * frame->bitrate * 1000) / frame->samplingRate;	
	if(frame->size == 0)
	{
		printf("size == 0\n");
		return -E_ERR;
	}
	if((hdr & MASK_PADDING) != 0)
		frame->size++;
	if(frame->samplingRate > 32000)
		frame->samples = 1152;
	else
		frame->samples = 576;
	frame->channel = hdr & MASK_CHANNEL;
	mp3->buf->Seek(-4, BUF_CUR);	
	frame->data = mp3->buf->GetData(frame->size);
	if(frame->data == NULL) return -E_ERR;
	return 0;
}

FLVAPI int skip(Mp3Audio *mp3, int offset)
{
	struct mp3_frame frame;
	int advance;
	while (offset > 0)
	{
		if(getNextFrame(mp3, &frame) != OK) return -E_ERR;
		advance = frame.samples / frame.samplingRate;
		offset -= advance;
	}	
	return 0;
}

FLVAPI int setAudioHdr(Mp3Audio *mp3, struct AudioHDR *hdr)
{
	struct mp3_frame frame;
	if(getNextFrame(mp3, &frame) < 0)
	{
		printf("here\n");
		return -E_ERR;
	}

	switch (frame.samplingRate)
	{
		case 44100:
			hdr->samplingRate = FLV_SOUNDRATE_44KHZ;
			break;
		case 22050:
			hdr->samplingRate = FLV_SOUNDRATE_22KHZ;
			break;
		case 11025:
			hdr->samplingRate = FLV_SOUNDRATE_11KHZ;
			break;
		default:
			hdr->samplingRate = FLV_SOUNDRATE_22KHZ;
			printf("Unsuported sampling rate: %i Hz\n", hdr->samplingRate);
			// return -E_FORMAT;
	}
	mp3->sr = frame.samplingRate;	
	switch(frame.channel)
	{
		case CHANNEL_STEREO:
			hdr->channel = FLV_SOUNDTYPE_STEREO;
			break;
		case CHANNEL_MONO:
			hdr->channel = FLV_SOUNDTYPE_MONO;
			break;
		default:
			printf("Only Mono / Stereo supported\n");
	}		
	hdr->format = FLV_AUDIO_FORMAT_MP3;
	hdr->sampleSize = FLV_SOUNDSIZE_16;
	return 0;
}

void __freemp3(Stream *stream)
{
	Mp3Audio *mp3 = (Mp3Audio *)stream;
	if(stream == NULL) return;
	mp3 = (Mp3Audio *)stream;
	mp3->buf->Free();
	free(mp3);
}

FLVAPI int writeSamples(Mp3Audio *mp3, CBuffer *buf, int len_ms, int *size)
{
	int samples, total, ret, len;
	struct mp3_frame frame;
	samples = (mp3->sr * len_ms) / 1000 + mp3->seekSamples;
	total = samples;	
	if(getNextFrame(mp3, &frame) < 0) return 0;
	len = 0;
	while(samples >= frame.samples)
	{
		ret = buf->Write(frame.data, frame.size);
		if(ret != frame.size) break;
		len += frame.size;
		samples -= frame.samples;
		ret = mp3->buf->Seek(frame.size, BUF_CUR);
		if(ret < 0) break;
		if(getNextFrame(mp3, &frame)<0) break;
	}
	total -= samples;
	printf("total samples %i, size %i, seek %i\n", total, len, samples);
	*size += len;
	mp3->seekSamples = samples;
	return total * 1000 / mp3->sr;
}

int writeDataMp3(Stream *stream, CBuffer *buf, int *len, void *data)
{
	int advance_ms, size = 0;
	Mp3Audio *mp3;
	if(stream == NULL || buf == NULL) return -E_STREAM;
	mp3 = (Mp3Audio *)stream;
	size += buf->WriteUI16(mp3->seekSamples);
	advance_ms = writeSamples(mp3, buf, *len, &size);	
	if(advance_ms <= 0) return -E_STREAM;
	*len = advance_ms;
	return size;
}

CMp3Audio* CMp3Audio::Create(const char *file, int offset)
{
	CMp3Audio *mp3;
	CStream *stream;
	if(file == NULL) return NULL;
	mp3 = new CMp3Audio;
	if(mp3 ==  NULL) return NULL;
	stream = &mp3->m_mp3audio.stream;
	stream->m_stream.writeData = writeDataMp3;
	stream->m_stream.freeStream = __freemp3;
	stream->m_stream.type = FLV_TAG_TYPE_AUDIO;
	stream->m_stream.resetStream = NULL;
	stream->m_stream.readData = NULL;
	mp3->m_mp3audio.buf = CBuffer::Create(file, BUF_FILE);
	if(mp3->m_mp3audio.buf == NULL)
	{
		printf("Open MP3 file failed! ");
		goto error;
	}
	mp3->m_mp3audio.seekSamples = 0;
	if(offset > 0)
	{
		if(skip(&mp3->m_mp3audio, offset) < 0)
		{
			printf("Mp3Audio_new: Skip faild!");
			goto error_buffer;
		}
	}
	if(setAudioHdr(&mp3->m_mp3audio, &stream->m_stream.hdr.audio) < 0)
	{
		printf("Mp3Audio_new: failed creating stream header\n");
		goto error_buffer;
	}
	stream->Rewind();
	mp3->m_mp3audio.stream=*stream;
	return mp3;
error_buffer:
	mp3->m_mp3audio.buf->Free();
error:
	free(mp3);
	return NULL;
}